package com.bcx.crm.java.other;

import com.bcx.crm.spring.aop.Performance;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * Created by ys on 2018/1/30.
 * 获取指定接口同包下的所有实现类
 * 接口--实现类--和接口同包的实现类
 */
public class InterfaceOfClass {
    public static void main(String[] args) {
        List<Class> list = InterfaceOfClass.getAllClassByInterface(Performance.class);
    }
    public static  List<Class> getAllClassByInterface(Class clazz){
        List<Class> listClass = new ArrayList<>();
        //判断是一个接口
        if (clazz.isInterface()){
            //获取clazz所在包的所有类
            List<Class> allClass = getAllClass(clazz.getPackage().getName());
            //循环判断路径下的所有类是否实现了指定的接口，并判处接口自己
            for (Class class1:allClass){
                if (clazz.isAssignableFrom(class1)){
                    if (!clazz.equals(class1)){
                        listClass.add(class1);
                    }
                }
            }
        }else {
            //如果不是接口，则获取它所有的子类
            List<Class> allClass = getAllClass(clazz.getPackage().getName());
            //循环判断路径下的所有类是否继承了指定类，并且排除父类自己
            for (Class class1:allClass){
                if (clazz.isAssignableFrom(class1)){
                    //自身不添加进去
                    if (!clazz.equals(class1)){
                        listClass.add(class1);
                    }
                }
            }
        }
        return listClass;
    }
    private static List<Class> getAllClass(String packageName){
        List<Class> listClass = new ArrayList<>();
        //返回当前正在执行的线程对象的引用 , 返回该线程的上下文classLoader
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packageName.replace(".", "/");
        try {
            List<File> fileList = new ArrayList<>();
            Enumeration<URL> enumeration = classLoader.getResources(path);
            if (enumeration.hasMoreElements()){
                URL url = enumeration.nextElement();
                //获取此url的文件名
                fileList.add(new File(url.getFile()));
            }
            for (File file:fileList){
                listClass.addAll(findClass(file,path));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return listClass;
    }

    /**
     * 如果file是文件夹，则递归调用findClass,找到文件夹下的所有类
     * 如果file是类文件，则加入list保存并返回
     * @param file 传递的文件名(文件或者文件夹)
     * @param packageName 包名
     * @return 文件或文件夹下的所有类名
     */
    private static List<Class> findClass(File file,String packageName){
        List<Class> listClass = new ArrayList<>();
        File[] files = file.listFiles();
        if (!file.exists()){
            return listClass;
        }
        //返回一个抽象路径名数组，这些路径名表示抽象路径名中目录中的文件
        for (File file2 : files) {
            if (file2.isDirectory()){
                if (!file2.getName().contains(".")){
                    List<Class> aClass = findClass(file2, packageName + "." + file2.getName());
                    listClass.addAll(aClass);
                }
            }else if (file2.getName().endsWith(".class")){//保存的类文件不需要后缀.class
                try {
                    //反射取的路径名是正常的包名称:例->com.bcx.crm.spring.aop
                    packageName=packageName.replace("/",".");
                    listClass.add(Class.forName(packageName+"."+file2.getName().substring(0,file2.getName().length()-6)));
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
        return listClass;
    }
}
